
#pragma once
#include "Requestor.hpp"
#include <unordered_set>

namespace zgwrpc
{
    namespace client
    {

        //服务注册
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr &requestor) : _requestor(requestor)
            {
            }
            bool RegistryMethod(const BaseConnection::ptr &conn, const std::string &method, const Address &host)
            {
                auto msg = MessageFactory::createMessage(MType::RSP_SERVICE);
                auto msg_req = dynamic_pointer_cast<ServiceResponse>(msg);
                msg_req->setId(Uuid::Getid());
                cout<<"rid:"<<msg_req->rid()<<endl;
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setMethod(method);
                msg_req->setHost(host);
                msg_req->setServiceOpType(ServiceOpType::SERVICE_REGISTER);
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->Send(conn, msg_req, msg_rsp);//同步调用
                if (ret == false)
                {
                    LOG(LogLevel::ERROR) << "注册服务失败,发送消息失败";
                    return false;
                }
                auto service_rsp = dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (service_rsp.get() == nullptr)
                {
                    LOG(LogLevel::ERROR) << "类型向下转型失败";
                    return false;
                }
                if ((int)service_rsp->rCode() != 0)
                {
                    LOG(LogLevel::ERROR) << "注册服务失败,服务端返回错误码:" << errReason(service_rsp->rCode());
                    return false;
                }
                return true;
            }

        private:
            Requestor::ptr _requestor;
        };

        //服务发现，处理服务注册
        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost(const std::vector<Address> &hosts) : _hosts(hosts), _idx(0)
            {}
            MethodHost():_idx(0){}
            
            void AppendHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }

            Address ChooseHost()
            {
                unique_lock<mutex> lock(_mutex);
                size_t idx = _idx % _hosts.size();
                return _hosts[idx++];
            }

            bool Empty()
            {
                return _hosts.empty();
            }

            void RemoveHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for(auto it = _hosts.begin(); it!= _hosts.end(); ++it)
                {
                    if (*it == host)
                    {
                        _hosts.erase(it);
                        return;
                    }
                }
            }

        private:
            std::mutex _mutex;
            size_t _idx; // 基于idx进行轮询
            std::vector<Address> _hosts;//服务主机列表
        };

        //这是客户端的服务发现，
        //这里面要做的就是将服务方法与能提供服务的主机地址进行映射，上层向进行对应rpc调用时直接在里边查找
        class Discover
        {
        public:
            using ptr = std::shared_ptr<Discover>;
            using DeleCallback =std::function<void(const Address&)>;
            Discover(const Requestor::ptr &requestor,DeleCallback dele_callback) : _requestor(requestor),_dele_callback(dele_callback) 
            {}

            //进行服务发现，此时的host是输出型参数，返回给调用者一个能够处理请求的主机地址
            bool ServiceDiscovery(const BaseConnection::ptr &conn,const std::string &method, Address &host)
            {
                // 如果请求时有host,就直接choose
                auto it = _method_hosts.find(method);
                if(it !=_method_hosts.end())
                {
                    host=it->second->ChooseHost();
                    return true;
                }
                // 否则,就进行服务发现
                auto msg_req = MessageFactory::create<ServiceRequest>();

                msg_req->setId(Uuid::Getid());
                cout<<"rid:"<<msg_req->rid()<<endl;
                msg_req->setMType(MType::REQ_SERVICE);
                msg_req->setMethod(method);
                msg_req->setServiceOpType(ServiceOpType::SERVICE_DISCOVERY);
                BaseMessage::ptr msg_rsp;
                bool ret = _requestor->Send(conn, msg_req, msg_rsp);
                if (ret == false)
                {
                    LOG(LogLevel::ERROR) << "服务发现失败,发送消息失败";
                    return false;
                }
                auto service_rsp = dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if(service_rsp.get() == nullptr)
                {
                    LOG(LogLevel::ERROR) << "类型向下转型失败";
                    return false;
                }
                if ((int)service_rsp->rCode() != 0)
                {
                    LOG(LogLevel::ERROR) << "服务发现失败,服务端返回错误码:" << errReason(service_rsp->rCode());
                    return false;
                }
                unique_lock<mutex> lock(_mutex);
                shared_ptr<MethodHost> method_host(new MethodHost(service_rsp->hosts()));
                if(method_host->Empty())
                {
                    LOG(LogLevel::ERROR)<<"服务发现失败,服务端没有返回任何主机";
                    return false;
                }
                host = method_host->ChooseHost();
                _method_hosts[method] = method_host;
                return true;
            }
            void OnServiceRequst(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg) // 基于服务请求的回调函数
            {
                //在客户端中只考虑两件事，服务上线，和服务发现
                auto  op_type=msg->serviceOpType();
                string method=msg->method();
                unique_lock<mutex> lock(_mutex);

                //服务上线
                if(op_type==ServiceOpType::SERVICE_ONLINE)
                {
                    //先查找 如果没有再添加
                    auto it=_method_hosts.find(method);
                    if(it==_method_hosts.end())
                    {
                        shared_ptr<MethodHost> method_host(new MethodHost());
                        method_host->AppendHost(msg->host());
                        _method_hosts[method]=method_host;
                    }
                    //如果有，就直接添加进对应的ServiceHost
                    else
                    {
                        it->second->AppendHost(msg->host());
                    }
                }
                
                //服务下线
                else if(op_type==ServiceOpType::SERVICE_OFFLINE)
                {
                    //先查找 如果没有就不用管
                    auto it=_method_hosts.find(method);
                    if(it==_method_hosts.end())
                    {
                        LOG(LogLevel::ERROR)<<"服务下线失败,找不到对应的服务";
                        return ;
                    }
                    //如果找到了，就从对应的ServiceHost中删除
                    it->second->RemoveHost(msg->host());
                    _dele_callback(msg->host());
                }

            }
        private:
            std::mutex _mutex;
            DeleCallback _dele_callback;
            std::unordered_map<std::string, MethodHost::ptr> _method_hosts;//根据方法名称映射对应的describe
            Requestor::ptr _requestor;
        };
    }
}